package com.yixing.tech.entity.entity.attr;

import org.apache.commons.lang3.StringUtils;
import com.yixing.tech.common.base.BaseMongoDao;
import com.yixing.tech.common.base.BusinessException;
import com.yixing.tech.common.base.ConditionsVO;
import com.yixing.tech.common.base.CustomAggregationOperation;
import com.yixing.tech.common.enums.ComparisonSymbol;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.entity.entity.RequestType;
import com.yixing.tech.entity.service.EntityBaseService;
import com.yixing.tech.common.utils.CommonUtil;
import com.yixing.tech.common.vo.s5.EntitySchema;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author yixing tech
 * at 2021/10/9
 * describe：
 */
@Component
public class Obj extends AttrType {
    public Obj() {
        super("对象实体", "obj");
    }

    public Obj(String desc, String code) {
        super(desc, code);
    }


    @Resource
    private BaseMongoDao baseMongoDao;
    @Autowired
    private EntityBaseService entityBaseService;
    private static Obj self;

    @PostConstruct
    private void init() {
        self = this;
    }

    @Override
    public void out(List<AggregationOperation> operations, EntitySchema.AttrVO attr) throws BusinessException {
        super.out(operations, attr);
        String templateId = attr.getReference().toString();
        if (templateId.startsWith("$")) return;
        String objTableName = "entity_" + templateId;
        String relationalField = attr.getRelationalField();
        EntitySchema cond = self.entityBaseService.queryByTableId(templateId, null);
        String agentCode = cond.getAgentAttrCode();
        String addFields = "";
        if (!StringUtils.isEmpty(agentCode)) {
            addFields = "{$addFields: {name:'$" + agentCode + "'}},";
        }
        String sql;
        if (ObjectUtils.isEmpty(relationalField)) {
            sql = "{ $lookup:{ " +
                    "from:'" +
                    objTableName +
                    "'," +
                    "let:{ location_id:'$" + attr.getCode() + ".id'}," +
                    "pipeline:[" +
                    "{$match:{$expr:{$eq:['$_id','$$location_id']}}}," +
                    addFields +
//                        "{$match:{$expr:{$in:['$_id',{$ifNull :['$$location_id',[]]}]}}}," +
//                        "{$addFields: {id: {$convert: {input: '$_id',to: 'string'}}}}," +
                    "]," +
                    "as:'" + attr.getCode() + "'}}";
        } else {
            List<ConditionsVO.Condition> filter = attr.getFilter();
            String condition = "{$expr:{$eq:['$" + relationalField + "','$$location_id']}}";
            String con = "{'$and':[";
            if (!CommonUtil.listIsBlank(filter)) {
                List<Criteria> ls = self.baseMongoDao.condition2CriteriaList(filter);
                for (Criteria c : ls) {
                    con += c.getCriteriaObject().toJson();
                    con += ",";
                }
            }
            con += "{'isDelete':{$ne:1}},";
            con += condition;
            con += "]}";
            sql = "{ $lookup:{ " +
                    "from:'" +
                    objTableName +
                    "'," +
                    "let:{ location_id:'$" + attr.getCode() + "'}," +
                    "pipeline:[" +
                    "{$match:" + con + "}," +
                    addFields +
                    "]," +
                    "as:'" + attr.getCode() + "'}}";
        }

        operations.add(new CustomAggregationOperation(sql));
        operations.add(Aggregation.unwind(attr.getCode(), true));
    }

    @Override
    public void out(List<AggregationOperation> operations, EntitySchema.AttrVO attr, RequestType requestType) throws BusinessException {
        if (requestType == RequestType.SIMPLE) {
            super.out(operations, attr);
            String templateId = attr.getReference().toString();
            if (templateId.startsWith("$")) return;
            String objTableName = "entity_" + templateId;
            String relationalField = attr.getRelationalField();
            EntitySchema cond = self.entityBaseService.queryByTableId(templateId, null);
            String agentCode = cond.getAgentAttrCode();
            String addFields = "";
            if (!StringUtils.isEmpty(agentCode)) {
                addFields = "{$addFields: {name:'$" + agentCode + "'}},";
            }
            String sql;
            if (ObjectUtils.isEmpty(relationalField)) {
                sql = "{ $lookup:{ " +
                        "from:'" +
                        objTableName +
                        "'," +
                        "let:{ location_id:'$" + attr.getCode() + ".id'}," +
                        "pipeline:[" +
                        "{$match:{$expr:{$eq:['$_id','$$location_id']}}}," +
                        addFields +
                        "{$project:{_id:1" + (!StringUtils.isEmpty(agentCode) ? ",name:1}}" : "}}") +
//                        "{$match:{$expr:{$in:['$_id',{$ifNull :['$$location_id',[]]}]}}}," +
//                        "{$addFields: {id: {$convert: {input: '$_id',to: 'string'}}}}," +
                        "]," +
                        "as:'" + attr.getCode() + "'}}";
            } else {
                List<ConditionsVO.Condition> filter = attr.getFilter();
                String condition = "{$expr:{$eq:['$" + relationalField + "','$$location_id']}}";
                String con = "{'$and':[";
                if (!CommonUtil.listIsBlank(filter)) {
                    List<Criteria> ls = self.baseMongoDao.condition2CriteriaList(filter);
                    for (Criteria c : ls) {
                        con += c.getCriteriaObject().toJson();
                        con += ",";
                    }
                }
                con += "{'isDelete':{$ne:1}},";
                con += condition;
                con += "]}";
                sql = "{ $lookup:{ " +
                        "from:'" +
                        objTableName +
                        "'," +
                        "let:{ location_id:'$" + attr.getCode() + "'}," +
                        "pipeline:[" +
                        "{$match:" + con + "}," +
                        addFields +
                        "{$project:{_id:1" + (!StringUtils.isEmpty(agentCode) ? ",name:1}}" : "}}") +
                        "]," +
                        "as:'" + attr.getCode() + "'}}";
            }

            operations.add(new CustomAggregationOperation(sql));
            operations.add(Aggregation.unwind(attr.getCode(), true));
        } else {
            out(operations, attr);
        }
    }

    /**
     * 过滤条件处理
     *
     * @param cond
     * @param attr
     */
    @Override
    public void condition(ConditionsVO.Condition cond, EntitySchema.AttrVO attr) throws BusinessException {
        super.condition(cond, attr);
        if (CommonUtil.stringIsBlank(attr.getRelationalField()) && null != cond.getValue()) {
            cond.setKey(cond.getKey() + ".id");
            if ((cond.getValue() instanceof String) && !ObjectId.isValid(cond.getValue().toString())) {
                throw new BusinessException(-1, "条件参数不能转为id");
            }
        }

    }

    @Override
    public Object wrapValue(EntitySchema.AttrVO attr, Object value, MongoTemplate mongoTemplate, EntityBaseService ebs, String tenantId) throws BusinessException {
        if (ObjectUtils.isEmpty(value)) {
            return value;
        }

        // 关联的对象编码
        String reference = Optional.ofNullable(attr.getReference()).map(String::valueOf).orElse(null);
        if (StringUtils.isBlank(reference)) {
            throw new BusinessException("缺少关联对象的编码");
        }
        // 查询关联对象信息
        EntitySchema referenceObj = entityBaseService.queryByTableId(reference);
        if (referenceObj == null) {
            throw new BusinessException("关联对象的定义不存在");
        }

        // 通过 agentAttrCode 作为key 来查询对象
        String agentAttrCode = referenceObj.getAgentAttrCode();
        if (StringUtils.isBlank(agentAttrCode)) {
            throw new BusinessException("关联对象的agentAttrCode字段为空，请先定义");
        }

        // 查询对象
        ConditionsVO condVo = new ConditionsVO();
        List<ConditionsVO.Condition> conds = new ArrayList<>();
        ConditionsVO.Condition c1 = new ConditionsVO.Condition();
        c1.setKey(agentAttrCode);
        c1.setValue(value);
        c1.setSymbol(ComparisonSymbol.is.name());
        conds.add(c1);
        ConditionsVO.Condition c2 = new ConditionsVO.Condition();
        c2.setKey("tenantId");
        c2.setValue(tenantId);
        c2.setSymbol(ComparisonSymbol.is.name());
        conds.add(c2);
        ConditionsVO.Condition c3 = new ConditionsVO.Condition();
        c3.setKey("isDelete");
        c3.setValue(YesOrNo.YES.ordinal());
        c3.setSymbol(ComparisonSymbol.ne.name());
        conds.add(c3);
        condVo.setConditions(conds);
        List list = self.entityBaseService.list(condVo, reference, null);
        if (ObjectUtils.isEmpty(list)) {
            throw new BusinessException(-1, attr.getName() + "[" + value + "]不存在，请核对数据");
        }
        return list.get(0);
    }
}
